﻿namespace Common.Helpers
{
    using System;
    using System.Windows.Controls;
    using System.Windows.Media;

    using Common.ChartDrawing;
    using Common.EventArg;
    using Common.Protocol;

    public class ChartRenderer
    {
        public event EventHandler<DateTimeEventArgs> RedLinePositionChanged;

        #region private fields

        private ProtocolInterval interval;

        private CanvasElements elements;

        private ElementsInitializer initializer;

        private Canvas canvas;

        #endregion

        #region public properties

        public double PrevHeight = 0, FirstHeight = 0;

        public float Scale;

        #endregion
        
        public ChartRenderer(Canvas canvas, int width)
        {
            this.canvas = canvas;
            Constants.Width = width;
            this.Scale = 1;
        }
        
        public DateTime SetRedLinePosition(double y)
        {
            if (y > Constants.StartCharts)
            {
                y = Constants.StartCharts;
            }

            if (y < Constants.EndCharts)
            {
                y = Constants.EndCharts;
            }

            this.initializer.SetRedLinePosition(y);

            double millisecondsFromStart = (Constants.StartCharts - y)
                / this.canvas.RenderSize.Height * this.interval.TotalMilliseconds;

            double millisecondsLeft = millisecondsFromStart;

            int blockId = this.interval.CurrentBlock.Id;

            foreach (var block in this.interval.Blocks)
            {
                if (millisecondsLeft - block.TotalMilliseconds <= 0)
                {
                    this.interval.CurrentTime = block.Start.AddTicks((long)millisecondsLeft * TimeSpan.TicksPerMillisecond);
                    blockId = block.Id;
                    break;
                }
                else
                {
                    millisecondsLeft -= block.TotalMilliseconds;
                }
            }

            Constants.CurrentTime = this.interval.CurrentTime;

            this.SetCurrentTime(Constants.CurrentTime);

            if (this.RedLinePositionChanged != null)
            {
                this.RedLinePositionChanged(this, new DateTimeEventArgs(Constants.CurrentTime));
            }

            return Constants.CurrentTime;
        }

        public void SetCurrentTime(DateTime time)
        {
            if (time < this.interval.Blocks[0].Start)
            {
                Constants.CurrentTime = this.interval.Blocks[0].Start;
                this.interval.CurrentTime = this.interval.Blocks[0].Start;
                this.initializer.SetRedLinePosition(Constants.StartCharts);
                return;
            }

            if (time > this.interval.Blocks[this.interval.Blocks.Count - 1].End)
            {
                Constants.CurrentTime = this.interval.Blocks[this.interval.Blocks.Count - 1].End;
                this.interval.CurrentTime = this.interval.Blocks[this.interval.Blocks.Count - 1].End;
                this.initializer.SetRedLinePosition(Constants.EndCharts);
                return;
            }

            Constants.CurrentTime = time;
            this.elements.CurrentTimeLabel.Text = Constants.CurrentTime.ToLongTimeString() + "." + Constants.CurrentTime.Millisecond / 100;
            this.elements.CurrentDateLabel.Text = Constants.CurrentTime.ToShortDateString();
        }

        public void ResetChannels()
        {
            this.elements = this.initializer.Initialize();
            this.InitializeCanvas();
        }
        
        public void ScaleElements()
        {
            if (this.PrevHeight == 0)
            {
                this.PrevHeight = 1;
            }

            double coef = this.canvas.RenderSize.Height / this.PrevHeight;
            Constants.Height = this.canvas.RenderSize.Height;

            if (this.elements != null)
            {
                ChartScaler.ScaleElements(coef, this.elements);
            }

            this.PrevHeight = this.canvas.RenderSize.Height;
        }
        
        public void Initialize(ProtocolInterval interval)
        {
            this.interval = interval;
            this.initializer = new ElementsInitializer(interval);

            this.canvas.Children.Clear();

            Constants.MillisecondsByPixel = this.interval.TotalMilliseconds / this.canvas.RenderSize.Height;
            Constants.StartCharts = this.canvas.RenderSize.Height - this.interval.StartEmptyMilliseconds / Constants.MillisecondsByPixel;
            Constants.EndCharts = this.interval.EndEmptyMilliseconds / Constants.MillisecondsByPixel;
            Constants.VerticalIndent = this.interval.StepMilliseconds / Constants.MillisecondsByPixel;
            Constants.UsefulWidth = 390;
            Constants.HorizontalIndent = 39;
            Constants.DatesColor = Colors.Gray;
            Constants.GridLinesColor = Colors.Gainsboro;
            Constants.FieldsWidth = (Constants.Width - Constants.UsefulWidth) / 2;
            Constants.Height = this.canvas.RenderSize.Height;

            Constants.CurrentTime = this.interval.CurrentTime;

            this.elements = this.initializer.Initialize();
            this.InitializeCanvas();
            GC.Collect();
        }
        
        private void InitializeCanvas()
        {
            if (this.elements != null)
            {
                foreach (var el in this.elements.Charts)
                {
                    this.canvas.Children.Add(el);
                }

                foreach (var el in this.elements.DateLabels)
                {
                    this.canvas.Children.Add(el);
                }

                foreach (var el in this.elements.EndOfBlockLines)
                {
                    this.canvas.Children.Add(el);
                }

                foreach (var el in this.elements.GridHorizontals)
                {
                    this.canvas.Children.Add(el);
                }

                foreach (var el in this.elements.GridVerticals)
                {
                    this.canvas.Children.Add(el);
                }

                foreach (var el in this.elements.SolidEndOfBlockLines)
                {
                    this.canvas.Children.Add(el);
                }

                foreach (var el in this.elements.TimeLabels)
                {
                    this.canvas.Children.Add(el);
                }

                if (this.elements.RedLine != null)
                {
                    this.canvas.Children.Add(this.elements.RedLine);
                }

                if (this.elements.CurrentDateLabel != null)
                {
                    this.canvas.Children.Add(this.elements.CurrentDateLabel);
                }

                if (this.elements.CurrentTimeLabel != null)
                {
                    this.canvas.Children.Add(this.elements.CurrentTimeLabel);
                }
            }
        }
    }
}